package com.hyt.it.ogt.kq.service.bm.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.enums.DelFlagEnum;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.cj.enums.ComponentNameEnum;
import com.hyt.it.ogt.cj.model.entity.ProjectCollectInfoOption;
import com.hyt.it.ogt.cj.service.IProjectCollectInfoOptionService;
import com.hyt.it.ogt.kq.common.utils.PmsPriceSeaTemplateMergeStrategy;
import com.hyt.it.ogt.kq.service.bm.den.UploadFileToHuaweiUtil;
import com.hyt.it.ogt.kq.service.bm.mapper.ProjectMapper;
import com.hyt.it.ogt.kq.service.bm.mapper.ProjectSubjectMapper;
import com.hyt.it.ogt.kq.service.bm.model.dto.SubjectAreaLimitDto;
import com.hyt.it.ogt.kq.service.bm.model.dto.SubjectAreaLimitTemplateDto;
import com.hyt.it.ogt.kq.service.bm.model.entity.Project;
import com.hyt.it.ogt.kq.service.bm.model.entity.ProjectSubject;
import com.hyt.it.ogt.kq.service.bm.model.entity.ProjectSubjectAreaLimit;
import com.hyt.it.ogt.kq.service.bm.model.entity.StudentInfo;
import com.hyt.it.ogt.kq.service.bm.model.entity.StudentSubject;
import com.hyt.it.ogt.kq.service.bm.service.IProjectSubjectAreaLimitService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentInfoService;
import com.hyt.it.ogt.kq.service.bm.service.IStudentSubjectService;
import com.hyt.it.ogt.kq.service.bm.service.ISubjectAreaLimitService;
import com.hyt.loginfo.model.Tree;
import com.hyt.loginfo.service.IBaseLoginService;
import com.hyt.loginfo.util.TreeUtil;
import com.hyt.progress.service.ProgressManager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;


/**
 * <p>
 *  科目区域限额管理服务
 * </p>
 *
 * @author w
 * @since 2020-05-15
 */
@Slf4j
@Service
public class SubjectAreaLimitServiceImpl extends BaseServiceImpl<ProjectSubjectMapper, ProjectSubject> implements ISubjectAreaLimitService {

    @Resource
    ProjectSubjectMapper projectSubjectMapper;
    @Resource
    private IBaseLoginService iBaseLoginService;
    @Resource
    private ProjectMapper projectMapper;
    @Resource
    IProjectCollectInfoOptionService iProjectCollectInfoOptionService;
    @Resource
    private UploadFileToHuaweiUtil uploadFileToHuaweiUtil;
    @Resource
    private IProjectSubjectAreaLimitService iProjectSubjectAreaLimitService;
    @Resource
    private IStudentSubjectService studentSubjectService;
    @Resource
    private IStudentInfoService studentInfoService;

    
    /**
     * 下载科目区域限额模板
     * @see com.hyt.it.ogt.kq.service.bm.service.ISubjectAreaLimitService#downloadTemplate(java.lang.String)
     **/
    @Override
    public String downloadSubjectLimitTemplate(Project project ) {
        String projectId = project.getId();
        // 首先获取报名的区域采集信息
        String areaId = projectMapper.getCollectIdByProject(ComponentNameEnum.area.getCode(), projectId, DelFlagEnum.NORMAL.getCode());
        if(StrUtil.isEmptyIfStr(areaId)) {
            throw new BusinessLogicException(31451102, "报名项目没有设置区域采集字段不需要配置区域限额");
        }
        // 获取所有区域的采集项
        List<ProjectCollectInfoOption> projectCollectInfoOptions = iProjectCollectInfoOptionService.queryByProjectCollectInfoId(areaId);
        if(CollUtil.isEmpty(projectCollectInfoOptions)) {
            throw new BusinessLogicException(31451103, "报名项目区域采集字段选项配置为空暂不支持配置区域限额");
        }
        List<Tree> opttionTrees = new ArrayList<>();
        for (ProjectCollectInfoOption projectCollectInfoOption : projectCollectInfoOptions) {
            Tree tree = new Tree();
            tree.setId(projectCollectInfoOption.getOptionValue());
            tree.setPid(projectCollectInfoOption.getParentOptionValue());
            tree.setObj(projectCollectInfoOption);
            tree.setText(projectCollectInfoOption.getOptionLabel());
            opttionTrees.add(tree);
        }
        
        List<Tree> treeToMultilevel = TreeUtil.treeToMultilevel(opttionTrees);
        
        // 获取所有的叶子节点
        List<Tree> leafCollectInfoOptions = getLeafCollectInfoOptions (treeToMultilevel);
        
        // 查询项目下所有的科目信息
        List<ProjectSubject> projectSubjects =  projectSubjectMapper.querySubjectList(projectId, null);
        if(CollUtil.isEmpty(projectSubjects)) {
            throw new BusinessLogicException(31451104, "报名项目科目信息未配置，暂不支持配置区域限额");
        }
        ExcelWriter excelWriter = null;
        String fileUrl = null;
        try {
         // 开始计算数据
            String fileNameStr = project.getProjectName() + "科目区域限额配置表_" + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + ".xls";
            // 使用 easyExcel 写到 OutputStream
            File projectSubjectAreaLimitExcel = FileUtil.createTempFile(fileNameStr, true);
            FileOutputStream fileOutputStream = new FileOutputStream(projectSubjectAreaLimitExcel);

            excelWriter = EasyExcel.write(fileOutputStream).build();
            // 获取所有的查询考生成绩信息
            List<SubjectAreaLimitTemplateDto> subjectAreaLimitDtos = getAllSubjectAreaLimitDtos(project, projectSubjects, leafCollectInfoOptions);
            // 根据条件获取所有的工单列表
            WriteSheet writeSheet = EasyExcel.writerSheet(1, "科目区域限额").head(SubjectAreaLimitTemplateDto.class).registerWriteHandler(new PmsPriceSeaTemplateMergeStrategy()).build();
            excelWriter.write(subjectAreaLimitDtos, writeSheet);
            excelWriter.finish();

            // 将文件上传到华为云
            log.info("# downloadSubjectLimitTemplate、文件{}生成成功", fileNameStr);
            // 将文件字节数组上传到华为云
            fileUrl = uploadFileToHuaweiUtil.uploadFileToHuawei(projectSubjectAreaLimitExcel, "xls", fileNameStr);
            log.info("# downloadSubjectLimitTemplate、文件{}上传华为云成功：{}", fileNameStr, fileUrl);
        } catch (Exception e) {
            log.error("# downloadSubjectLimitTemplate、文件{}生成失败", e);
            throw new BusinessLogicException(31451105, "报名项目导出区域限额模板系统异常，请联系系统管理员！");
        }
        return fileUrl;
    }

    /**
     * 获取所有树状数据的叶子节点
     * @param treeToMultilevel 树数据节点
     * @return
     */
    private List<Tree> getLeafCollectInfoOptions(List<Tree> treeToMultilevel) {
        List<Tree> leafNode = new ArrayList<>();
        for (Tree tree : treeToMultilevel) {
            getAllLeafNodes(tree, leafNode);
        }
        return leafNode;
    }

    /**
     * 获取所有的叶子节点
     * @param tree
     * @param leafNode
     */
    private void getAllLeafNodes(Tree tree, List<Tree> leafNode) {
        if(CollUtil.isEmpty(tree.getChildren())) {
            leafNode.add(tree);
        }else {
            for (Tree child : tree.getChildren()) {
                getAllLeafNodes(child, leafNode);
            }
        }
    }

    /**
     * 
     * @param project
     * @return
     */
    private List<SubjectAreaLimitTemplateDto> getAllSubjectAreaLimitDtos(Project project, List<ProjectSubject> projectSubjects, List<Tree> leafCollectInfoOptions) {
        // 首先查询
        List<SubjectAreaLimitTemplateDto> subjectAreaLimitDtos = new ArrayList<>();
        for (ProjectSubject projectSubject : projectSubjects) {
            for (Tree projectCollectInfoOption : leafCollectInfoOptions) {
                SubjectAreaLimitTemplateDto subjectAreaLimitDto = new SubjectAreaLimitTemplateDto();
                subjectAreaLimitDto.setProjectId(project.getId());
                subjectAreaLimitDto.setProjectName(project.getProjectName());
                subjectAreaLimitDto.setSubjectId(projectSubject.getSubjectId());
                subjectAreaLimitDto.setSubjectName(projectSubject.getSubjectName());
                subjectAreaLimitDto.setBmAreaID(projectCollectInfoOption.getId());
                subjectAreaLimitDto.setBmAreaName(projectCollectInfoOption.getText());
                subjectAreaLimitDto.setBmAreaLimit(0);
                subjectAreaLimitDtos.add(subjectAreaLimitDto);
            }
        }
        return subjectAreaLimitDtos;
    }

    /**
     * 导入报名项目科目限额模板
     * @throws Exception 
     * @see com.hyt.it.ogt.kq.service.bm.service.ISubjectAreaLimitService#importSubjectAreaLimitTemplate(org.springframework.web.multipart.MultipartFile, com.hyt.it.ogt.kq.service.bm.model.entity.Project)
     **/
    @Override
    public void importSubjectAreaLimitTemplate(MultipartFile file, Project project) throws Exception {
        List<SubjectAreaLimitDto> subjectAreaLimitDtos = excelToSubjectAreaLimitList(file);
        if (CollUtil.isEmpty(subjectAreaLimitDtos)) {
            throw new BusinessLogicException(31452103, "导入的模板文件数据为空，请重新整理导入！");
        }
        log.info("[导入科目区域限额信息]完成 %{}", 10L);
        ProgressManager.updateProgressCurrent(10, "解析模板数据完成");
        
        String projectId = project.getId();
        // 首先获取报名的区域采集信息
        String areaId = projectMapper.getCollectIdByProject(ComponentNameEnum.area.getCode(), projectId, DelFlagEnum.NORMAL.getCode());
        if(StrUtil.isEmptyIfStr(areaId)) {
            throw new BusinessLogicException(31451102, "报名项目没有设置区域采集字段不需要配置区域限额");
        }
        // 获取所有区域的采集项
        List<ProjectCollectInfoOption> projectCollectInfoOptions = iProjectCollectInfoOptionService.queryByProjectCollectInfoId(areaId);
        if(CollUtil.isEmpty(projectCollectInfoOptions)) {
            throw new BusinessLogicException(31451103, "报名项目区域采集字段选项配置为空暂不支持配置区域限额");
        }
        Map<String, ProjectCollectInfoOption> projectCollectInfoOptionMaps = projectCollectInfoOptions.stream().collect(Collectors.toMap(ProjectCollectInfoOption::getOptionValue, Function.identity()));
        
        // 查询项目下所有的科目信息
        List<ProjectSubject> projectSubjects =  projectSubjectMapper.querySubjectList(projectId, null);
        if(CollUtil.isEmpty(projectSubjects)) {
            throw new BusinessLogicException(31451104, "报名项目科目信息未配置，暂不支持配置区域限额");
        }
        // 项目科目
        Map<String, ProjectSubject> projectSubjectMaps = projectSubjects.stream().collect(Collectors.toMap(ProjectSubject::getSubjectId, Function.identity()));
        // 校验业务数据是否和项目数据一致
        for (SubjectAreaLimitDto subjectAreaLimitDto : subjectAreaLimitDtos) {
            if(!projectId.equals(subjectAreaLimitDto.getProjectId())) {
                throw new BusinessLogicException(31452104, "导入的模板文件数据项目ID和选择的项目不一致！");
            }
            if(null == projectSubjectMaps.get(subjectAreaLimitDto.getSubjectId())) {
                throw new BusinessLogicException(31452105, "导入的模板文件数据科目ID和选择的项目科目不一致！");
            }
            if(null == projectCollectInfoOptionMaps.get(subjectAreaLimitDto.getBmAreaID())) {
                throw new BusinessLogicException(31452106, "导入的模板文件数据地区ID和选择的项目地区配置不一致！");
            }
            if(!NumberUtil.isInteger(subjectAreaLimitDto.getBmAreaLimit()) || Integer.valueOf(subjectAreaLimitDto.getBmAreaLimit()).compareTo(0) < 0 ) {
                throw new BusinessLogicException(31452107, "导入的模板文件数据地区限额配置只能为整数且大于等于0！");
            }
        }
        log.info("[导入科目区域限额信息]完成 %{}", 30L);
        ProgressManager.updateProgressCurrent(30, "校验模板数据完成");
        List<ProjectSubjectAreaLimit> projectSubjectAreaLimitList = new ArrayList<>();
        // 将数据解析入库
        for (SubjectAreaLimitDto subjectAreaLimitDto : subjectAreaLimitDtos) {
            // 首先查询库里面是否存在限额配置了
            ProjectSubjectAreaLimit projectSubjectAreaLimit = iProjectSubjectAreaLimitService.getLimit(subjectAreaLimitDto.getProjectId(), subjectAreaLimitDto.getSubjectId(), subjectAreaLimitDto.getBmAreaID());
            // 如果已经存在则做修改，如果不存在则直接新增
            if(null == projectSubjectAreaLimit) {
                projectSubjectAreaLimit = new ProjectSubjectAreaLimit();
                projectSubjectAreaLimit.setId(UUIDUtils.newSortUUID());
                projectSubjectAreaLimit.setProjectId(subjectAreaLimitDto.getProjectId());
                projectSubjectAreaLimit.setSubjectId(subjectAreaLimitDto.getSubjectId());
                projectSubjectAreaLimit.setSubjectName(subjectAreaLimitDto.getSubjectName());
                projectSubjectAreaLimit.setBmArea(subjectAreaLimitDto.getBmAreaID());
                projectSubjectAreaLimit.setBmLimit(Integer.valueOf(subjectAreaLimitDto.getBmAreaLimit()));
                projectSubjectAreaLimit.setCreateDate(LocalDateTime.now());
                projectSubjectAreaLimit.setCreateBy(iBaseLoginService.getUserId());
                projectSubjectAreaLimit.setUpdateDate(LocalDateTime.now());
                projectSubjectAreaLimit.setUpdateBy(iBaseLoginService.getUserId());
                projectSubjectAreaLimit.setDelFlag(Boolean.FALSE);
            } else {
                projectSubjectAreaLimit.setBmLimit(Integer.valueOf(subjectAreaLimitDto.getBmAreaLimit()));
                projectSubjectAreaLimit.setUpdateDate(LocalDateTime.now());
                projectSubjectAreaLimit.setUpdateBy(iBaseLoginService.getUserId());
            }
            projectSubjectAreaLimitList.add(projectSubjectAreaLimit);
        }
        log.info("[导入科目区域限额信息]完成 %{}", 60L);
        ProgressManager.updateProgressCurrent(60, "组装数据完成");
        if(CollUtil.isNotEmpty(projectSubjectAreaLimitList)) {
            iProjectSubjectAreaLimitService.saveOrUpdateBatch(projectSubjectAreaLimitList);
        }
        ProgressManager.finish(null);
        log.info("[导入科目区域限额信息]完成 %{}", 100L);
    }
    
    
    public List<SubjectAreaLimitDto> excelToSubjectAreaLimitList(MultipartFile file) throws Exception {
        List<SubjectAreaLimitDto> subjectAreaLimitDtos = new ArrayList<>();
        EasyExcel.read(file.getInputStream(), SubjectAreaLimitDto.class, new AnalysisEventListener<SubjectAreaLimitDto>() {
            @Override
            public void invoke(SubjectAreaLimitDto object, AnalysisContext context) {
                subjectAreaLimitDtos.add(object);
            }
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {

            }
        }).sheet(0).headRowNumber(2).doRead();
        return subjectAreaLimitDtos;
    }
    
    
    
    /**
     * 检查是否超额
     * @see com.hyt.it.ogt.kq.service.bm.service.IProjectSubjectAreaLimitService#checkExcessBySubjectIdAndArea(java.lang.String, java.lang.String)
     **/
    @Override
    public Boolean checkExcessBySubjectIdAndArea(String projectId, String studentId, String subjectId, String bmAreaCollId, String bmArea) {
        log.info("# checkExcessBySubjectIdAndArea subjectId:{},bmArea:{}", subjectId, bmArea);
        if(StrUtil.isEmptyIfStr(bmArea) || StrUtil.isEmptyIfStr(subjectId)) {
            return Boolean.FALSE;
        }
        
        // 新查询报考报考该区域的考生
        // 首先查询科目的报考人数
        QueryWrapper<StudentInfo> studentInfoQueryWrapper = new QueryWrapper<>();
        studentInfoQueryWrapper.lambda()
                .select(StudentInfo::getStudentId)
                .eq(CharSequenceUtil.isNotEmpty(projectId), StudentInfo::getProjectId, projectId)
                .eq(CharSequenceUtil.isNotEmpty(bmAreaCollId), StudentInfo::getProjectCollectInfoId, bmAreaCollId)
                .eq(CharSequenceUtil.isNotEmpty(bmArea), StudentInfo::getValue, bmArea)
                .eq(StudentInfo::getDelFlag, DelFlagEnum.NORMAL.getCode());
        List<StudentInfo> studentInfos = studentInfoService.list(studentInfoQueryWrapper);
        List<String> studentIds = new ArrayList<>();
        if(CollUtil.isNotEmpty(studentInfos)) {
            studentIds = studentInfos.stream().map(StudentInfo::getStudentId).collect(Collectors.toList());
        }
        studentIds.add("-999999999");
        if(CollUtil.isNotEmpty(studentIds) && StrUtil.isNotBlank(studentId)) {
            for (Iterator<String> iterator = studentIds.iterator(); iterator.hasNext();) {
                String studentIdTemp =  iterator.next();
                if(studentIdTemp.equals(studentId)) {
                    iterator.remove();
                }
            }
        }
        
        
        // 首先查询科目的报考人数
        QueryWrapper<StudentSubject> studentSubjectQueryWrapper = new QueryWrapper<>();
        studentSubjectQueryWrapper.lambda()
                .eq(StudentSubject::getSubjectId, subjectId)
                .in(CollUtil.isNotEmpty(studentIds), StudentSubject::getStudentId, studentIds);
        int dbStudentSubjectCount =  studentSubjectService.count(studentSubjectQueryWrapper);
        
        
        // 查询科目区域配置的额度
        // 首先查询科目的报考人数
        QueryWrapper<ProjectSubjectAreaLimit> projectSubjectAreaLimitQueryWrapper = new QueryWrapper<>();
        projectSubjectAreaLimitQueryWrapper.lambda()
                .eq(ProjectSubjectAreaLimit::getSubjectId, subjectId)
                .eq(CharSequenceUtil.isNotEmpty(bmArea), ProjectSubjectAreaLimit::getBmArea, bmArea);
        
        // 
        List<ProjectSubjectAreaLimit> projectSubjectAreaLimits = iProjectSubjectAreaLimitService.list(projectSubjectAreaLimitQueryWrapper);
        if(CollUtil.isEmpty(projectSubjectAreaLimits)) {
            return Boolean.FALSE;
        }
        int configCount = 0;
        for (ProjectSubjectAreaLimit projectSubjectAreaLimit : projectSubjectAreaLimits) {
            if(CharSequenceUtil.isNotEmpty(projectSubjectAreaLimit.getBmArea())) {
                configCount += projectSubjectAreaLimit.getBmLimit();
            }
        }
        
        // 如果数据库已经报考的科目数没有超出配置的额度
        if(dbStudentSubjectCount < configCount ) {
            return  Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}
